/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package vn.webapp.controller.cp;

import algo.BackTrackSearch.BackTrackSearch;
import algo.BackTrackSearch.SolutionTT;
import algo.Common.Course;
import algo.Common.Data;
import algo.Common.DataOut;
import algo.Common.DaySlot;
import algo.Common.Instructor;
import algo.appMain;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Vector;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import vn.webapp.controller.BaseWeb;
import vn.webapp.dao.ViewingDAO;
import vn.webapp.dto.DataPage;
import vn.webapp.model.BusyTeachers;
import vn.webapp.model.Clas;
import vn.webapp.model.Classes;
import vn.webapp.model.CourseTeacherClass;
import vn.webapp.model.CourseTeacherViewing;
import vn.webapp.model.Courses;
import vn.webapp.model.Coursing;
import vn.webapp.model.CoursingsSave;
import vn.webapp.model.Rooms;
import vn.webapp.model.Sessions;
import vn.webapp.model.Teachers;
import vn.webapp.model.TeachersToBusy;
import vn.webapp.model.Timetabling;
import vn.webapp.model.Timetablingraw;
import vn.webapp.service.ClassesService;
import vn.webapp.service.CoursesService;
import vn.webapp.service.CoursingService;
import vn.webapp.service.RoomsService;
import vn.webapp.service.SessionsService;
import vn.webapp.service.TeachersService;
import vn.webapp.service.TimetablingService;
import vn.webapp.service.ViewingService;

/**
 *
 * @author Tonytran
 */
@Controller("cpTimetabling")
@RequestMapping(value = {"/cp"})
public class TimetablingController extends BaseWeb {

    @Autowired
    private CoursingService coursingService;
    @Autowired
    private TimetablingService timeTablingService;
    @Autowired
    private RoomsService roomsService;
    @Autowired
    private SessionsService sessionsService;
    @Autowired
    private ClassesService classesService;
    
    @Autowired
    private CoursesService coursesService;
    
    @Autowired
    private TeachersService teachersService;
    
    @Autowired
    private ViewingService viewingService;
    private int nav = 9;

    /**
     *
     * @param model
     * @return
     */
    @RequestMapping(value = "/timetabling", method = RequestMethod.GET)
    public String timetabling(ModelMap model, HttpSession session,
            @RequestParam(value = "page", defaultValue = "0") int page) {
        DataPage<Coursing> dataCoursing = coursingService.list();
        DataPage<Rooms> rooms = roomsService.list();

        int pageSize = 15;
        DataPage<Timetabling> timeTabling = timeTablingService.filter(page, pageSize);
        //DataPage<Timetabling> timeTabling = timeTablingService.list();
        DataPage<Sessions> sessions = sessionsService.list();

        List<Coursing> coursing = dataCoursing.getData();
        Set<Coursing> dataCla = new HashSet<Coursing>(coursing);
        model.put("dataCoursing", dataCla);
        model.put("dataTimetabling", timeTabling);
        model.put("dataRooms", rooms);
        model.put("dataSessions", sessions);

        model.put("pageIndex", page);
        model.put("nav", this.nav);
        return "cp.timetabling";
    }

    @RequestMapping(value = "/viewing", method = RequestMethod.GET)
    public String viewingTimeTable(ModelMap model, HttpSession session) {
        DataPage<Classes> classes = classesService.list();

        List<Timetablingraw> timeTabling = timeTablingService.listRaw();
        //List<CourseTeacherClass> Viewing = timeTablingService.listViewing();
        List<CourseTeacherViewing> courseTeachers = viewingService.list();
        List<List<Timetablingraw>> timetablingRaw = new ArrayList<>();
        for(CourseTeacherViewing courseTeacher : courseTeachers)
        {
            List<Timetablingraw> tbl = viewingService.getTimetablingByCourseTeacherId(courseTeacher.getCourseteacher_id());
            timetablingRaw.add(tbl);
        }

        // Init date & Init slot
        int[] dateArray = {2,3,4,5,6}; 
        int[] slotArray = {1,2,3,4,5,6};
        
        // Init a list of classes for morning & afternoon = Map[classCode, listClasses]
        Map<String, List<Timetablingraw>> ListByClass = new HashMap<String, List<Timetablingraw>>();
        Map<String, List<Timetablingraw>> ListByClassAfternoon = new HashMap<String, List<Timetablingraw>>();
        
        List<Classes> viewClasses = new ArrayList<>();
        for(Classes clas : classes.getData())
        {
            List<Timetablingraw> tmp = new ArrayList<>();
            Classes tempClass = null;
            for(Timetablingraw tblrL : timeTabling)
            {
                if(tblrL.getClass_id() == clas.getClass_id())
                {
                    tmp.add(tblrL);
                    tempClass = clas;
                    if(tblrL.getTimerange() == 0)
                        ListByClass.put(clas.getCode(), tmp);
                    else
                        ListByClassAfternoon.put(clas.getCode(), tmp);
                }
            }
            viewClasses.add(tempClass);
        }
        
        //HashMap<Integer, HashMap<Integer, HashMap<Integer, List<Timetablingraw>>>> ListCombo = new HashMap<Integer, HashMap<Integer, HashMap<Integer, List<Timetablingraw>>>>();
        
        // Init a Hashmap of classes for morning = HashMap[classCode, Map[date, listClasses]]
        HashMap<String, Map<Integer, List<Timetablingraw>>> ListDateCombo = new HashMap<String, Map<Integer, List<Timetablingraw>>>();
        // Loops all Map[classCode, listClasses] for rendering 
        for(Map.Entry<String, List<Timetablingraw>> listByClassesCombo : ListByClass.entrySet())
        {
            // Get a classCode
            String classCode = listByClassesCombo.getKey();
            
            // Get list of classes
            List<Timetablingraw> listTimetablingByClassesCombo = listByClassesCombo.getValue();

            // Init a list of classes by date = Map[date, listClasses]
            Map<Integer, List<Timetablingraw>> ListByDate = new HashMap<Integer, List<Timetablingraw>>();
            List<Integer> datesNotAvailable = new ArrayList<>();
           
            for(int date: dateArray)
            {
                List<Timetablingraw> temp = new ArrayList<>();
                List<Timetablingraw> temp1 = new ArrayList<>();
                for(Timetablingraw tblrL : listTimetablingByClassesCombo)
                {
                    if(tblrL.getDay() == date)
                    {
                        temp.add(tblrL);
                    }
                   
                }
                for(int slt : slotArray)
                {
                    for(Timetablingraw tSlot : temp)
                    {
                        if(slt == tSlot.getSlot())
                        {
                            temp1.add(tSlot);
                            ListByDate.put(tSlot.getDay(), temp1);
                        }
                    }
                }
            }
            for(int date : dateArray)
            {
                if(ListByDate.get(date) == null)
                {
                    ListByDate.put(date, null);
                }
            }
            
            ListDateCombo.put(classCode, ListByDate);
        }
        

        HashMap<String, Map<Integer, List<Timetablingraw>>> ListDateComboAfternoon = new HashMap<String, Map<Integer, List<Timetablingraw>>>();
        for(Map.Entry<String, List<Timetablingraw>> listByClassesCombo : ListByClassAfternoon.entrySet())
        {
            String classCode = listByClassesCombo.getKey();
            List<Timetablingraw> listTimetablingByClassesCombo = listByClassesCombo.getValue();

            Map<Integer, List<Timetablingraw>> ListByDate = new HashMap<Integer, List<Timetablingraw>>();
            List<Integer> datesNotAvailable = new ArrayList<>();
           
            for(int date: dateArray)
            {
                List<Timetablingraw> temp = new ArrayList<>();
                List<Timetablingraw> temp1 = new ArrayList<>();
                for(Timetablingraw tblrL : listTimetablingByClassesCombo)
                {
                    if(tblrL.getDay() == date)
                    {
                        temp.add(tblrL);
                    }
                   
                }
                for(int slt : slotArray)
                {
                    for(Timetablingraw tSlot : temp)
                    {
                        if(slt == tSlot.getSlot())
                        {
                            temp1.add(tSlot);
                            ListByDate.put(tSlot.getDay(), temp1);
                        }
                    }
                }
            }
            for(int date : dateArray)
            {
                if(ListByDate.get(date) == null)
                {
                    ListByDate.put(date, null);
                }
            }

            ListDateComboAfternoon.put(classCode, ListByDate);
        }

        model.put("ListDateCombo", ListDateCombo);
        model.put("ListDateComboAfternoon", ListDateComboAfternoon);
        model.put("dateArray", dateArray);
        model.put("slotArray", slotArray);
        model.put("dataClasses", viewClasses);
        model.put("ListByClass", ListByClass);
        model.put("nav", 10);
        return "cp.viewingtable";
    }

    @RequestMapping(value = "/runalgorithm", method = RequestMethod.GET)
    public String runAlgorithm(ModelMap model, HttpServletRequest request) {
         String iStatus = null;
         String szMessage = null;
         DataPage<Classes> classes = classesService.list();
         List<Classes> __classes = classes.getData();
         
         DataPage<Courses> dataCourses = coursesService.list();
         List<Courses> __courses = dataCourses.getData();
    
         DataPage<BusyTeachers> dataBusyTeachers = teachersService.listAllBusyTeachers();
         List<BusyTeachers> __busyteachers = dataBusyTeachers.getData();
         
         
         DataPage<TeachersToBusy> dataTeachersToBusy = teachersService.getAllTeachersToBusy();
         Set<TeachersToBusy> __teachers = new HashSet<TeachersToBusy>(dataTeachersToBusy.getData());
         
         DataPage<Coursing> dataCoursing = coursingService.list();
         List<Coursing> _coursings = dataCoursing.getData();
         Set<Coursing> __coursings = new HashSet<Coursing>(_coursings);
         
         try {
            StringBuilder sb = new StringBuilder();
            sb.append("Headers:\n");
            Enumeration<String> headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String headerName = headerNames.nextElement();
                Enumeration<String> headers = request.getHeaders(headerName);
                while (headers.hasMoreElements()) {
                    String headerValue = headers.nextElement();
                    sb.append(headerName).append(':').append(headerValue).append('\n');
                }
            }
            Vector<Course> _course;
            sb.append("\nParameters:\n");
            Data D = new Data();
            //D.readData("C:\\timetabling-data.xml");
            D.readData("C:\\Data\\timetabling-data.xml");
            //D.formatData(__classes, __courses, __teachers, __coursings);
            D.v_fBuildData(__classes, __courses, __teachers, __coursings);
            _course = D.getCourses();
             for(Course _cours: _course)
             {
                 sb.append(_cours.getName());
             }
            
            appMain m = new appMain();

            SolutionTT sol = new SolutionTT(1, 6, 2, 6);
            sol.init(D);
            BackTrackSearch s = new BackTrackSearch();
            s.search(sol, D);
            int[] sol_slots_bt = s.getSolutionSlots();
            int[] sol_days_bt = s.getSolutionDays();
            sb.append("\n Days:\n");
            //m.writeSolution(D, sol_slots_bt, sol_days_bt, "timetabling-backtrack.html");
            Vector<DataOut> __outPutData = m.vOutPutData(D, sol_slots_bt, sol_days_bt, __classes);
            
            if(!__outPutData.isEmpty())
            {
                // Truncate the old DB
                //int iTruncateOldDB = timeTablingService.truncateOldData();
                DataPage<Timetabling> timeTabling = timeTablingService.getAll();
                List<Timetabling> xxx = timeTabling.getData();
                if(timeTabling.getData() != null)
                {
                    timeTablingService.truncateAllOldata(timeTabling.getData());
                }
                // Remove all data
                
                List<CoursingsSave> cs = coursingService.getOriginSource();

                for (DataOut _outputTimetabling : __outPutData) {
                    Collections.shuffle(cs);
                    int randomCourseId = cs.get(0).getCourse_id();

                    timeTablingService.saveAtimetablingAuto(_outputTimetabling, randomCourseId);
                }
               
            }

            iStatus = "ok";
            szMessage = "Run algorithm successfully.";
        } catch (Exception e) {
            iStatus = e.getMessage();
            szMessage = "Run algorithm not successfully.";
        }
        //String iStatus = timeTablingService.executeAlgorithm();
        model.put("iStatus", iStatus);
        model.put("szMessage", szMessage);
        return "cp.viewingtable";
    }
}
